﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;
namespace GX
{
    internal class AssetInfo : IAssetInfo
    {
        readonly AssetManagerService _assetManager;
        readonly IAssetManifest _assetManifest;

        string _path;
        int _referenceCount;
        int _loadCallCount;

        AssetStatus _status = AssetStatus.None;
        AssetBundle _assetbundle;
        Object[] _allAssets;
        byte[] _bytes;
        string _text;
        WWW _w3;
        string _error = string.Empty;

        HashSet<AssetInfo> _dependencySet;//本资源的依赖

        //通知给外部
        Action<AssetInfo> _onDone;

        //通知给依赖它的资源
        Action<AssetInfo> _onDoneForDependency;

        public AssetInfo(string name, AssetManagerService assetManager)
        {
            _path = name;
            _assetManager = assetManager;
            _assetManifest = assetManager.assetManifest;
        }
        public string path { get { return _path; } }
        public AssetStatus status { get { return _status; } }
        public bool isDone { get { return _status == AssetStatus.Done; } }
        public string error { get { return _error; } }
        public bool isSuccess { get { return isDone && string.IsNullOrEmpty(_error); } }
        public byte[] bytes { get { return _bytes; } }
        public string text { get { return _text; } }
        public int referenceCount { get { return _referenceCount; } }
        public int loadCallCount { get { return _loadCallCount; } }
        public int dependencyReferenceCount { get { return _referenceCount - _loadCallCount; } }
        public bool hasBytes { get; set; }

        public bool hasText { get; set; }
        public float selfProgress
        {
            get
            {
                if (this.isDone) return 1f;
                if (null != _w3)
                    return _w3.progress;
                return 0f;
            }
        }
        public float progress
        {
            get
            {
                if (this.isDone) return 1f;
                int count = 1;
                float v = selfProgress;
                if (null != _dependencySet)
                {
                    count += _dependencySet.Count;
                    var iter = _dependencySet.GetEnumerator();
                    while (iter.MoveNext())
                    {
                        v += iter.Current.progress;
                    }
                }
                v = v / count;
                return v;
            }
        }
        public Object GetAsset()
        {
            if (this.isSuccess && null != _allAssets && _allAssets.Length != 0)
                return _allAssets[0];
            return null;
        }
        public Object LoadAsset(string name)
        {
            if (!this.isSuccess) return null;
            if (null != _allAssets && _allAssets.Length != 0)
            {
                if (_allAssets.Length == 1)
                    return _allAssets[0];
                for (int i = 0; i < _allAssets.Length; ++i)
                {
                    if (name.StartsWith(_allAssets[i].name))
                        return _allAssets[i];
                }
            }
            return null;
        }
        public Object[] GetAllAssets()
        {
            return _allAssets;
        }
        public void Retain()
        {
            ++_loadCallCount;
            AddRef();
        }
        public void Release()
        {
            if (_loadCallCount > 0)
            {
                --_loadCallCount;
                RemoveRef();
            }
        }
        void AddRef()
        {
            ++_referenceCount;
            if (1 == _referenceCount && this._status != AssetStatus.None)
            {
                _assetManager.OnRefChange(this, false);
            }
        }
        void RemoveRef()
        {
            --_referenceCount;
            if (0 == _referenceCount)
            {
                _assetManager.OnRefChange(this, true);
            }
        }
        public void AddDependency(AssetInfo dependency)
        {
            if (null == _dependencySet)
                _dependencySet = new HashSet<AssetInfo>();
            if (_dependencySet.Add(dependency))
            {
                dependency.AddRef();
                dependency._onDoneForDependency += OnDependencyLoaded;
            }
        }
        public bool isDependencyLoaded
        {
            get
            {
                if (null != _dependencySet)
                {
                    var iter = _dependencySet.GetEnumerator();
                    while (iter.MoveNext())
                    {
                        if (iter.Current._status != AssetStatus.Done)
                            return false;
                    }
                }
                return true;
            }
        }
        public void Wait()
        {
            if (_status != AssetStatus.None) return;
            _status = AssetStatus.Wait;
        }
        public void BeginLoad(MonoBehaviour behaviour, Action<AssetInfo> onloaded)
        {
            if (_status != AssetStatus.Wait) return;

            _status = AssetStatus.Loading;
            _onDone = onloaded;

            if (_assetManifest.enableAssetBundle)
            {
                behaviour.StartCoroutine(LoadCor());
                return;
            }

            var file = path;
            int index = path.LastIndexOf(".");
            if (index != -1)
                file = path.Substring(0, index);
            var obj = Resources.Load(string.Format("{0}{1}", _assetManifest.resourcePath, file));
            if (null != obj)
            {
                _allAssets = new Object[] { obj };
                TextAsset ta = obj as TextAsset;
                if (null != ta)
                {
                    if (hasBytes)
                        _bytes = ta.bytes;
                    if (hasText)
                        _text = ta.text;
                }
            }
            OnDone();
        }
        IEnumerator LoadCor()
        {
            int numFail = 0;
            _w3 = null;
            while (numFail < _assetManifest.tryLoadCount)
            {
                _w3 = new WWW(string.Format("{0}{1}", _assetManifest.streamPath, _path));
                yield return _w3;
                if (string.IsNullOrEmpty(_w3.error))
                {
                    break;
                }
                else
                {
                    ++numFail;
                }
            }
            OnSelfLoaded();
        }
        void OnSelfLoaded()
        {
            if (!this.isDependencyLoaded)
            {
                _status = AssetStatus.WaitDependency;
                return;
            }
            OnDone();
        }
        void OnDependencyLoaded(AssetInfo dependency)
        {
            if (_status != AssetStatus.WaitDependency)
                return;
            OnDone();
        }
        void OnDone()
        {
            _status = AssetStatus.Done;


            if (_assetManifest.enableAssetBundle)
            {
                if (this.isSuccess)
                {
                    if (this.hasBytes)
                        _bytes = _w3.bytes;
                    if (this.hasText)
                        _text = _w3.text;

                    _assetbundle = _w3.assetBundle;


                    _allAssets = _assetbundle.LoadAllAssets();
                    for (int i = 0; i < _allAssets.Length; ++i)
                    {
                        var obj = _allAssets[i];
                        //if (obj is GameObject)
                        //{
                        //    ResetShaders(obj as GameObject);
                        //}
                    }


                }
                _w3.Dispose();
                _w3 = null;
                Debugger.Log("资源加载完：" + _path, "00ff00");
            }
            if (null != _onDone)
            {
                _onDone(this);
                _onDone = null;
            }
            if (null != _onDoneForDependency)
            {
                _onDoneForDependency(this);
                _onDoneForDependency = null;
            }

            TryUnloadAssetBundle(0);
        }
        void TryUnloadAssetBundle(int beUsedCount)
        {
            if (null == _assetbundle) return;

            //当前资源最多被其他一个资源依赖：并且用到本资源的资源已经加载完成
            if (_assetManifest.GetAssetBundleDirectUsageCount(_path) <= beUsedCount)
            {
                Debugger.Log("UnloadAssetBundle:" + _path, "ffff00");
                _assetbundle.Unload(false);
                _assetbundle = null;
            }
            if (null != _dependencySet)
            {
                var iter = _dependencySet.GetEnumerator();
                while (iter.MoveNext())
                {
                    iter.Current.TryUnloadAssetBundle(1);
                }
            }
        }
        static List<Renderer> renderList = new List<Renderer>();
        void ResetShaders(GameObject go)
        {
            go.GetComponentsInChildren<Renderer>(true, renderList);
            for (int i = 0; i < renderList.Count; ++i)
            {
                var r = renderList[i];
                var mats = r.sharedMaterials;
                if (null != mats)
                {
                    for (int j = 0; j < mats.Length; ++j)
                    {
                        var mat = mats[j];
                        if (null != mat && null != mat.shader)
                            mat.shader = Shader.Find(mat.shader.name);
                    }
                }
            }

        }
        void ClearAssets()
        {
            _error = string.Empty;

            _bytes = null;
            _text = null;
            _allAssets = null;

            _assetbundle = null;
            _w3 = null;
        }
        public void Unload()
        {
            if (null != _dependencySet)
            {
                var iter = _dependencySet.GetEnumerator();
                while (iter.MoveNext())
                {
                    var dependency = iter.Current;
                    dependency.RemoveRef();
                    dependency._onDoneForDependency -= OnDependencyLoaded;
                }
                _dependencySet = null;
            }
            _bytes = null;
            _text = null;
            _allAssets = null;
            Debugger.Log("Unload资源：" + _path + " -> " + (null != _assetbundle), "ff0000");

            if (null != _assetbundle)
            {
                _assetbundle.Unload(true);
                _assetbundle = null;
            }
        }
    }
}